package models

import (
	"errors"
	"strings"

	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"

	"cvevulner/common"
	"cvevulner/util"
)

//Loophole issue body model
type Loophole struct {
	Number            string //编号
	Components        string // 漏洞归属的组件
	Version           string // 漏洞归属的版本
	CvsScore          string //cvss 评分
	CvsVector         string //cvs向量
	BriefIntroduction string //漏洞简述
	Influences        string //漏洞造成的影响
	Principle         string //漏洞原理
	OpScore           string //openEuler 评分
	OpVector          string //openEuler 向量
	InfVersion        string //受影响的组件版本
	AvoidScheme       string //规避方案
	InfPackage        string //受影响的软件包
	InfProduct        string //受影响的产品
	Repo              string //漏洞所属仓库
	RepoDesc          string //组件描述
	ScoreType         string //评分类型 v2 v3
	AbiVersion        string // abi变化
}

//VectorValue cvss V3.0 vector  model
type VectorValue struct {
	AttackVector       string //AV
	AttackComplexity   string //AC
	PrivilegesRequired string //PR
	UserInteraction    string //UI
	Scope              string //S
	Confidentiality    string //C
	Integrity          string //I
	Availability       string //A
}

//VectorValueV2 cvss v2 vector  model
type VectorValueV2 struct {
	AccessVector     string //AV
	AccessComplexity string //AC
	Authentication   string //Au
	Confidentiality  string //C
	IntegrityImpact  string //I
	Availability     string //A
}

func (g *GiteOriginIssue) Insert() error {
	o := orm.NewOrm()
	_, err := o.Insert(g)
	return err
}

func (g GiteOriginIssue) Update() error {
	o := orm.NewOrm()
	_, err := o.Update(g)
	return err
}

func (g *GiteOriginIssue) InsertOrUpdate(flag int) error {
	if g.Number == "" {
		return errors.New("issue number can not be null")
	}
	o := orm.NewOrm()
	if flag == 1 {
		ex := GiteOriginIssue{Number: g.Number, CveNumber: g.CveNumber, RepoPath: g.RepoPath, IssueId: g.IssueId, Owner: g.Owner}
		err := o.Read(&ex, "number", "cve_number", "repo_path", "issue_id")
		if err == nil && ex.Id > 0 {
			g.Id = ex.Id
		}
		logs.Info("processing, repo: ", g.RepoPath, g.Number, g.CveNumber, g.Owner)
		_, err = o.InsertOrUpdate(g)
		return err
	} else {
		list := []GiteOriginIssue{}
		num, err := o.QueryTable("cve_gite_origin_issue").Filter("cve_number", g.CveNumber).
			Filter("repo_path", g.RepoPath).Filter("owner", g.Owner).All(&list)
		//ex := GiteOriginIssue{CveNumber: g.CveNumber, RepoPath: g.RepoPath, Owner: g.Owner}
		//err := o.Read(&ex, "cve_number", "repo_path")
		if err == nil && len(list) > 0 {
			for _, li := range list {
				g.Id = li.Id
			}

		}
		logs.Info("num: ", num, ", processing, repo: ", g.RepoPath, g.Number, g.CveNumber, g.Owner)
		_, err = o.InsertOrUpdate(g)
		return err
	}
}

func (g *GiteOriginIssue) Detlete() error {
	if g.Number == "" {
		return errors.New("issue number can not be null")
	}
	o := orm.NewOrm()
	ex := GiteOriginIssue{Number: g.Number, CveNumber: g.CveNumber, RepoPath: g.RepoPath, IssueId: g.IssueId}
	num, err := o.Delete(&ex, "number", "cve_number", "repo_path", "issue_id")
	if err == nil && num > 0 {
		logs.Info("GiteOriginIssue, successfully deleted, "+
			"Number: ", g.Number, ",CveNumber:", g.CveNumber, ",RepoPath:", g.RepoPath, ",IssueId:", g.IssueId)
	}
	return err
}

func UpdateCveCenterStatus(cveId int64, cveNum string, status int8) {
	o := orm.NewOrm()
	_ = o.Raw("UPDATE cve_vuln_center SET cve_status = ? WHERE cve_id = ? and cve_num = ?",
		status, cveId, cveNum).QueryRow()
	return
}

func GetGiteOriginIssue(cveNum string) (oi GiteOriginIssue, err error) {
	o := orm.NewOrm()
	oi.CveNumber = cveNum
	err = o.Read(&oi, "cve_number")
	return
}

func (g *GiteOriginIssue) ParseToLoophole() (hole Loophole, err error) {
	lp := Loophole{}
	if g.Body == "" {
		return lp, errors.New("can not parse a null body")
	}
	lp.InfProduct = g.InfProduct
	lp.Repo = g.RepoPath
	lp.RepoDesc = g.RepoDesc
	scoreType, err := judgeScoreType(g.Body)
	if err != nil {
		logs.Error(err, "judgeScoreType, body: ", g.Body)
		//return lp, err
	}
	lp.ScoreType = scoreType
	if isNewTpl(g.Body) {
		parseNewTplToLoopHole(&lp, g.Body)
	} else {
		parseOldTplToLoopHole(&lp, g.Body)
	}
	return lp, nil
}

func (v *VectorValue) VctToVectorValue(vector string) bool {
	if util.TrimString(vector) == "" {
		return false
	}
	m, ok := util.VctToMap(vector)
	if !ok {
		return false
	}
	v.AttackVector = util.ReadVMValue(m["AV"])
	v.AttackComplexity = util.ReadVMValue(m["AC"])
	v.Availability = util.ReadVMValue(m["A"])
	v.Confidentiality = util.ReadVMValue(m["C"])
	v.Integrity = util.ReadVMValue(m["I"])
	v.PrivilegesRequired = util.ReadVMValue(m["PR"])
	v.UserInteraction = util.ReadVMValue(m["UI"])
	v.Scope = util.ReadVMValue(m["S"])
	return true
}

func (v *VectorValueV2) VctToVectorValue(vector string) bool {
	if util.TrimString(vector) == "" {
		return false
	}
	m, ok := util.VctToMap(vector)
	if !ok {
		return false
	}
	v.AccessVector = util.ReadVMValueV2(m["AV"])
	v.AccessComplexity = util.ReadVMValueV2(m["AC"])
	v.Availability = util.ReadVMValueV2(m["A"])
	v.Confidentiality = util.ReadVMValueV2(m["C"])
	v.IntegrityImpact = util.ReadVMValueV2(m["I"])
	v.Authentication = util.ReadVMValueV2(m["Au"])
	return true
}

func judgeScoreType(body string) (st string, err error) {
	if body == "" {
		return "", errors.New("can not judge score type by nil body")
	}
	body = strings.ReplaceAll(body, " ", "")
	tb := []byte(body)
	vs := util.RegexpScoreTypeV2.Find(tb)
	if len(vs) > 0 {
		return util.CvsScoreV2, nil
	}
	vs = util.RegexpScoreTypeV3.Find(tb)
	if len(vs) > 0 {
		return util.CvsScoreV3, nil
	}
	return "", errors.New("can not judge score type")
}

func isNewTpl(body string) bool {
	tb := []byte(body)
	v1 := util.RegexpIsNewTpl.Find(tb)
	v2 := util.RegexpIsNewTpl2.Find(tb)
	if len(v1) > 0 || len(v2) > 0 {
		return false
	} else {
		return true
	}
}

func parseOldTplToLoopHole(lp *Loophole, body string) {
	sm := util.RegexpCveNumber.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 0 {
		val := sm[0][1]
		lp.Number = util.GetCveNumber(util.TrimString(val))
	}
	sm = util.RegexpCveComponents.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 1 {
		for _, v := range sm[0][1:] {
			if v != "" {
				lp.Components = util.GetCvePkg(v)
				break
			}
		}
	}
	sm = util.RegexpCveVersion.FindAllStringSubmatch(body, 1)
	if len(sm) > 0 && len(sm[0]) > 1 {
		for _, v := range sm[0][1:] {
			if v != "" {
				lp.Version = strings.Split(util.TrimString(v), ",")[0]
				break
			}
		}
	}
	sm = util.RegexpCveScore.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 0 {
		vs := util.TrimString(sm[0][1])
		if cs := util.ExtractDigital(vs); cs != "" {
			lp.CvsScore = cs
		}
		if v := util.ExtractVector(vs, lp.ScoreType); v != "" {
			lp.CvsVector = v
		}
	}
	sm = util.RegexpCveBriefDesc.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 0 {
		lp.BriefIntroduction = sm[0][1]
	}
	sm = util.RegexpCveInfluencesDesc.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 0 {
		lp.Influences = sm[0][1]
	}
	sm = util.RegexpCvePrincipleDesc.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 0 {
		lp.Principle = sm[0][1]
	}
	sm = util.RegexpCveOpScore.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 {
		vp := util.TrimString(sm[0][1])
		if cs := util.ExtractDigital(vp); cs != "" {
			lp.OpScore = cs
		}
		if v := util.ExtractVector(vp, lp.ScoreType); v != "" {
			lp.OpVector = v
		}
	}
	sm = util.RegexpCveInfluencesVersion.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 0 {
		lp.InfVersion = util.TrimString(sm[0][1])
		infVersionSlice := strings.Split(lp.InfVersion, ",")
		if len(infVersionSlice) > 0 {
			infTmpSlice := []string{}
			for _, ise := range infVersionSlice {
				infTmpSlice = append(infTmpSlice, common.BranchVersionRep(ise))
			}
			if len(infTmpSlice) > 0 {
				lp.InfVersion = strings.Join(infTmpSlice, ",")
			}
		}
	}
	sm = util.RegexpCvePlannedMeasures.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 {
		if len(sm[0]) > 0 {
			lp.AvoidScheme = sm[0][1]
		}

	} else {
		sm = util.RegexpCvePlannedMeasures1.FindAllStringSubmatch(body, -1)
		if len(sm) > 0 && len(sm[0]) > 0 {
			lp.AvoidScheme = sm[0][1]
		}
	}
	sm = util.RegexpCveInfluencesPkg.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 {
		lp.InfPackage = util.TrimString(sm[0][1])
	}
	if lp.OpVector == "" {
		if lp.OpScore == "" {
			lp.OpScore = lp.CvsScore
			lp.OpVector = lp.CvsVector
		} else {
			if lp.OpScore == lp.CvsScore {
				lp.OpVector = lp.CvsVector
			}
		}
	}
}

func parseNewTplToLoopHole(lp *Loophole, body string) {
	sm := util.RegexpCveNumber.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 0 {
		val := sm[0][1]
		lp.Number = util.GetCveNumber(util.TrimString(val))
	}
	sm = util.RegexpCveComponents.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 1 {
		for _, v := range sm[0][1:] {
			if v != "" {
				lp.Components = util.GetCvePkg(v)
				break
			}
		}
	}
	sm = util.RegexpCveVersion.FindAllStringSubmatch(body, 1)
	if len(sm) > 0 && len(sm[0]) > 1 {
		for _, v := range sm[0][1:] {
			if v != "" {
				lp.Version = strings.Split(util.TrimString(v), ",")[0]
				break
			}
		}
	}
	sm = util.RegexpCveScore.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 0 {
		vs := util.TrimString(sm[0][1])
		if cs := util.ExtractDigital(vs); cs != "" {
			lp.CvsScore = cs
		}
		if v := util.ExtractVector(vs, lp.ScoreType); v != "" {
			lp.CvsVector = v
		}
	}
	sm = util.RegexpCveBriefDescNew.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 0 {
		lp.BriefIntroduction = strings.Replace(sm[0][1], "二、漏洞分析结构反馈", "", -1)
	}
	sm = util.RegexpCveInfluencesDescNew.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 && len(sm[0]) > 0 {
		lp.Influences = sm[0][1]
	}
	sm = util.RegexpCveOpScoreNew.FindAllStringSubmatch(body, -1)
	if len(sm) > 0 {
		vp := util.TrimString(sm[0][1])
		if cs := util.ExtractDigital(vp); cs != "" {
			lp.OpScore = cs
		}
		if v := util.ExtractVector(vp, lp.ScoreType); v != "" {
			lp.OpVector = v
		}
	}
	body = strings.ReplaceAll(body, " ", "")
	if IsAbiInfo(body) {
		if IsFixMatching(body) {
			sm = util.RegexpCveInfluencesVersionNew.FindAllStringSubmatch(body, -1)
			if len(sm) > 0 && len(sm[0]) > 0 {
				value := util.ExtractCommentEffectVersion(sm[0][1])
				infVersion := util.TrimString(value)
				lp.InfVersion = util.TrimString(value)
				infVersionSlice := strings.Split(infVersion, ",")
				if len(infVersionSlice) > 0 {
					infTmpSlice := []string{}
					for _, ise := range infVersionSlice {
						infTmpSlice = append(infTmpSlice, common.BranchVersionRep(ise))
					}
					if len(infTmpSlice) > 0 {
						lp.InfVersion = strings.Join(infTmpSlice, ",")
					}
				}
			}
		} else {
			sm = util.RegexpCveInfluencesVersionFixNew.FindAllStringSubmatch(body, -1)
			if len(sm) > 0 && len(sm[0]) > 0 {
				value := util.ExtractCommentEffectVersion(sm[0][1])
				infVersion := util.TrimString(value)
				lp.InfVersion = util.TrimString(value)
				infVersionSlice := strings.Split(infVersion, ",")
				if len(infVersionSlice) > 0 {
					infTmpSlice := []string{}
					for _, ise := range infVersionSlice {
						infTmpSlice = append(infTmpSlice, common.BranchVersionRep(ise))
					}
					if len(infTmpSlice) > 0 {
						lp.InfVersion = strings.Join(infTmpSlice, ",")
					}
				}
			}
		}
	} else {
		sm = util.RegexpCveAbiVersionNew.FindAllStringSubmatch(body, -1)
		if len(sm) > 0 && len(sm[0]) > 0 {
			value := util.ExtractCommentEffectVersion(sm[0][1])
			infVersion := util.TrimString(value)
			lp.InfVersion = util.TrimString(value)
			infVersionSlice := strings.Split(infVersion, ",")
			if len(infVersionSlice) > 0 {
				infTmpSlice := []string{}
				for _, ise := range infVersionSlice {
					infTmpSlice = append(infTmpSlice, common.BranchVersionRep(ise))
				}
				if len(infTmpSlice) > 0 {
					lp.InfVersion = strings.Join(infTmpSlice, ",")
				}
			}
		}
		if IsFixMatching(body) {
			sm = util.RegexpCveAbiNew.FindAllStringSubmatch(body, -1)
			if len(sm) > 0 && len(sm[0]) > 0 {
				value := util.ExtractCommentAbiVersion(sm[0][1])
				infVersion := util.TrimString(value)
				lp.AbiVersion = util.TrimString(value)
				infVersionSlice := strings.Split(infVersion, ",")
				if len(infVersionSlice) > 0 {
					infTmpSlice := []string{}
					for _, ise := range infVersionSlice {
						infTmpSlice = append(infTmpSlice, common.BranchVersionRep(ise))
					}
					if len(infTmpSlice) > 0 {
						lp.AbiVersion = strings.Join(infTmpSlice, ",")
					}
				}
			}
		} else {
			sm = util.RegexpCveAbiFixNew.FindAllStringSubmatch(body, -1)
			if len(sm) > 0 && len(sm[0]) > 0 {
				value := util.ExtractCommentAbiVersion(sm[0][1])
				infVersion := util.TrimString(value)
				lp.AbiVersion = util.TrimString(value)
				infVersionSlice := strings.Split(infVersion, ",")
				if len(infVersionSlice) > 0 {
					infTmpSlice := []string{}
					for _, ise := range infVersionSlice {
						infTmpSlice = append(infTmpSlice, common.BranchVersionRep(ise))
					}
					if len(infTmpSlice) > 0 {
						lp.AbiVersion = strings.Join(infTmpSlice, ",")
					}
				}
			}
		}
	}
	if lp.OpVector == "" {
		if lp.OpScore == "" {
			lp.OpScore = lp.CvsScore
			lp.OpVector = lp.CvsVector
		} else {
			if lp.OpScore == lp.CvsScore {
				lp.OpVector = lp.CvsVector
			}
		}
	}
}

func IsAbiInfo(body string) bool {
	tb := []byte(body)
	v1 := util.RegexpIsAbiTpl.Find(tb)
	if len(v1) > 0 {
		return false
	} else {
		return true
	}
}

func IsFixMatching(body string) bool {
	tb := []byte(body)
	v1 := util.RegexpIsFixTpl.Find(tb)
	if len(v1) > 0 {
		return false
	} else {
		return true
	}
}
